﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace RecipeRecommender
{
    public static class IngredientUtil
    {
        public static List<Ingredient> IngredientList = new List<Ingredient>();

        public static Dictionary<string, Ingredient> ValueIngredientMap = new Dictionary<string, Ingredient>();
        public static Dictionary<string, Ingredient> NameIngredientMap = new Dictionary<string, Ingredient>();

        private const string FILE_NAME = "CBRData\\IngredientList.txt";
        private const string VEGETARIAN_MAP_FILE_NAME = "CBRData\\VegetarianIngredientMap.txt";
        public static Dictionary<string, List<Ingredient>> VegetarianIngredientMap = new Dictionary<string, List<Ingredient>>();

        public static void setAllIngredients()
        {
            int idx0 = 0;
            int idx1 = 0;
            int idx2 = 0;
            int idx3 = 0;


            if (!File.Exists(FILE_NAME))
            {
                Console.WriteLine("{0} does not exist.", FILE_NAME);
            }
            using (StreamReader sr = File.OpenText(FILE_NAME))
            {
                String input;
                while ((input = sr.ReadLine()) != null)
                {
                    int level = 0;
                    if (input[0] == '-')
                    {
                        level++;
                    };
                    if (input[1] == '-')
                    {
                        level++;
                    };
                    if (input[2] == '-')
                    {
                        level++;
                    };

                    input = input.Substring(level);

                    if (level == 0)
                    {
                        idx0++;
                        idx1 = 0;
                        idx2 = 0;
                        idx3 = 0;
                    }
                    else if (level == 1)
                    {
                        idx1++;
                        idx2 = 0;
                        idx3 = 0;
                    }
                    else if (level == 2)
                    {
                        idx2++;
                        idx3 = 0;
                    }
                    else if (level == 3)
                    {
                        idx3++;
                    }

                    Ingredient ingredient = new Ingredient(input.Trim(), idx0, idx1, idx2, idx3);
                    Console.WriteLine(input + " ---- " + idx0 + "." + idx1 + "." + idx2 + "." + idx3);
                    
                    if (!NameIngredientMap.ContainsKey(ingredient.Name.ToLower()))
                    {
                        IngredientList.Add(ingredient);
                    
                        string valueString = idx0 + "." + idx1 + "." + idx2 + "." + idx3;
                        ValueIngredientMap.Add(valueString, ingredient);
                        NameIngredientMap.Add(ingredient.Name.ToLower(), ingredient);
                    }
                }
                Console.WriteLine("The end of the stream has been reached.");
                
            }
        }


        public static int getLevel(Ingredient cIng)
        {
            int nLevel = 0;

            for (int i = 0; i < 4; i++)
            {
                if (cIng.Value[i] == 0)
                {
                    nLevel = i;
                    return nLevel;
                }
            }
            return nLevel;
        }

        public static double getSimilarityScore(Ingredient input, Ingredient examined)
        {
            return getSimilarityScore(input.Value, examined.Value);
        }

        public static double getSimilarityScore(int[] inputValue, int[] examinedValue)
        {
            int ir = 0;
            int er = 0;
            int ip = 0;
            int ep = 0;

            ir = getDistanceToRoot(inputValue);
            er = getDistanceToRoot(examinedValue);
            int cpd = getCommonParentDistanceFromRoot(inputValue, examinedValue);
            ip = ir - cpd;
            ep = er - cpd;

            double similarityScore = 100 - (100 * (Convert.ToDouble((ip + ep)) / Convert.ToDouble((ir + er))));
            return similarityScore;
        }

        private static int getDistanceToRoot(int[] value)
        {
            int distance = -1;
            /*
            for (int i = 0; i < 4; i++)
                if (value[i] != 0) distance++;
             */
            if (value[0] != 0) distance++;
            if (value[1] != 0) distance++;
            if (value[2] != 0) distance++;
            if (value[3] != 0) distance++;
            return distance;
        }

        private static int getCommonParentDistanceFromRoot(int[] inputValue, int[] examinedValue)
        {
            int distance = -1;

            /*
           for (int i = 0; i < 4; i++) 
               if (inputValue[i] != 0 && examinedValue[i] != 0 && inputValue[i] == examinedValue[i]) distance++;
           */

            if (inputValue[0] != 0 && examinedValue[0] != 0 && inputValue[0] == examinedValue[0]) distance++;
            if (inputValue[1] != 0 && examinedValue[1] != 0 && inputValue[1] == examinedValue[1]) distance++;
            if (inputValue[2] != 0 && examinedValue[2] != 0 && inputValue[2] == examinedValue[2]) distance++;
            if (inputValue[3] != 0 && examinedValue[3] != 0 && inputValue[3] == examinedValue[3]) distance++;
            return distance;
        }

        /*
        public static Ingredient getIngredientByName(String name, List<Ingredient> ingredientList)
        {
            foreach (Ingredient ingredient in ingredientList)
                if (String.Equals(ingredient.Name, name, StringComparison.Ordinal)) return ingredient;

            return null;
        }
        */

        public static Ingredient getIngredientByName(String name)
        {
            if (NameIngredientMap.ContainsKey(name.ToLower()))
                return NameIngredientMap[name.ToLower()];
            else return null;
        }

        public static Ingredient getIngredientByValueString(String value)
        {
            if (ValueIngredientMap.ContainsKey(value))
                return ValueIngredientMap[value];
            else return null;
        }

        public static double getSimilarityScore(List<Ingredient> inputList, List<Ingredient> examinedList)
        {
            if (inputList.Count == 0) return 0;
            /*
            List<int[]> completeInputValueList = getCompleteValueList(inputList);
            List<int[]> completeExaminedValueList = getCompleteValueList(examinedList);

            return getSimilarityScore(completeInputValueList, completeExaminedValueList);
        */
            List<string> completeInputValueList = getCompleteValueListInStringWithAncestor(inputList);
            List<string> completeExaminedValueList = getCompleteValueListInStringWithAncestor(examinedList);

            return getSimilarityScore(completeInputValueList, completeExaminedValueList);
     
        }

       

        private static List<String> getCompleteValueListInStringWithAncestor(List<Ingredient> ingredientList)
        {
            List<String> completeValueList = new List<String>();
            foreach (Ingredient i in ingredientList)
            {
                int[] currentValue = new int[4];
                i.Value.CopyTo(currentValue, 0);
                completeValueList.Add(getIngredientValueString(currentValue));

                // Get ancestor values
                /* eg. 1.2.2.3 => will add 1.2.2.3, 1.2.2.0, 1.2.0.0, & 1.0.0.0 to the list
                 * eg. 1.3.4.0 => will add 1.3.4.0, 1.3.0.0, & 1.0.0.0 to the list
                 * */
                if (currentValue[3] != 0)
                {
                    currentValue[3] = 0;
                    completeValueList.Add(getIngredientValueString(currentValue));
                }

                if (currentValue[2] != 0)
                {
                    currentValue[2] = 0;
                    completeValueList.Add(getIngredientValueString(currentValue));
                }
                if (currentValue[1] != 0)
                {
                    currentValue[1] = 0;
                    completeValueList.Add(getIngredientValueString(currentValue));
                }


            }
            return completeValueList;

        }

        private static List<String> getStringValueList(List<Ingredient> ingredientList)
        {
            List<String> stringValueList = new List<String>();
            foreach (Ingredient i in ingredientList)
            {
                stringValueList.Add(getIngredientValueString(i.Value));
            }
            return stringValueList;

        }

        /*
        private static List<String> getCompleteValueListInStringWithChildren(List<Ingredient> ingredientList)
        {
            List<String> completeValueList = new List<String>();
            foreach (Ingredient i in ingredientList)
            {
                int[] currentValue = new int[4];
                i.Value.CopyTo(currentValue, 0);
                completeValueList.Add(getIngredientValueString(currentValue));

                // Get Children values
                // eg. 1.2.0.0 => will add 1.2.0.0, 1.2.1.1, 1.2.1.2, 1.2.2.1, etc to the list
                // eg. 1.3.4.0 => will add 1.3.4.0, 1.3.4.1, & 1.3.4.2, etc to the list
                

                if (currentValue[1] == 0)
                {
                    bool loop1 = true;
                    bool loop2 = true;
                    bool loop3 = true;

                    for (int counter1 = 0; loop1; counter1++)
                    {
                        currentValue[1] = counter1;
                        if (ValueIngredientMap.ContainsKey(getIngredientValueString(currentValue)))
                        {
                            for (int counter2 = 0; loop2; counter2++)
                            {
                                currentValue[2] = counter2;
                                if (ValueIngredientMap.ContainsKey(getIngredientValueString(currentValue)))
                                {
                                    for (int counter3 = 0; loop3; counter3++)
                                    {
                                        currentValue[3] = counter3;
                                        if (ValueIngredientMap.ContainsKey(getIngredientValueString(currentValue)))
                                        {
                                            completeValueList.Add(getIngredientValueString(currentValue));
                                        }
                                        else
                                        {
                                            currentValue[3] = 0;
                                            loop3 = false;
                                        }
                                    }
                                }
                                else
                                {
                                    currentValue[2] = 0;
                                    loop2 = false;
                                }
                            }
                        }
                        else
                        {
                            currentValue[1] = 0;
                            loop1 = false;
                        }
                    }
                }
                else if (currentValue[2] == 0)
                { // 1.2.0.0
                    bool loop2 = true;
                    bool loop3 = true;
                    for (int counter2 = 0; loop2; counter2++)
                    {
                        currentValue[2] = counter2;
                        if (ValueIngredientMap.ContainsKey(getIngredientValueString(currentValue)))
                        {
                            for (int counter3 = 0; loop3; counter3++)
                            {
                                currentValue[3] = counter3;
                                if (ValueIngredientMap.ContainsKey(getIngredientValueString(currentValue)))
                                {
                                    completeValueList.Add(getIngredientValueString(currentValue));
                                }
                                else
                                {
                                    currentValue[3] = 0;
                                    loop3 = false;
                                }
                            }
                        }
                        else
                        {
                            currentValue[2] = 0;
                            loop2 = false;
                        }
                    }

                }
                else if (currentValue[3] == 0)
                { // 1.2.1.0

                    bool loop3 = true;
                    for (int counter3 = 0; loop3; counter3++)
                    {
                        currentValue[3] = counter3;
                        if (ValueIngredientMap.ContainsKey(getIngredientValueString(currentValue)))
                        {
                            completeValueList.Add(getIngredientValueString(currentValue));
                        }
                        else
                        {
                            loop3 = false;
                        }
                    }
                }
                else
                {
                    completeValueList.Add(getIngredientValueString(currentValue));
                }

            }
            return completeValueList;
        }
        */

        private static List<String> getCompleteValueListInStringWithChildren(List<Ingredient> ingredientList)
        {
            List<String> completeValueList = new List<String>();
            foreach (Ingredient i in ingredientList)
            {
                int[] currentValue = new int[4];
                i.Value.CopyTo(currentValue, 0);
                completeValueList.Add(getIngredientValueString(currentValue));

                // Get Children values
                // eg. 1.2.0.0 => will add 1.2.0.0, 1.2.1.1, 1.2.1.2, 1.2.2.1, etc to the list
                // eg. 1.3.4.0 => will add 1.3.4.0, 1.3.4.1, & 1.3.4.2, etc to the list


                if (currentValue[1] == 0)
                {
                    for (int counter1 = 0; counter1<20; counter1++)
                    {
                        currentValue[1] = counter1;
                        for (int counter2 = 0; counter2 < 20; counter2++)
                        {
                            currentValue[2] = counter2;
                            for (int counter3 = 0; counter3 < 20; counter3++)
                            {
                                currentValue[3] = counter3;
                                if (ValueIngredientMap.ContainsKey(getIngredientValueString(currentValue)))
                                {
                                    completeValueList.Add(getIngredientValueString(currentValue));
                                }
                            }
                        }
                    }
                }
                else if (currentValue[2] == 0)
                { // 1.2.0.0
                    
                        for (int counter2 = 0; counter2 < 20; counter2++)
                        {
                            currentValue[2] = counter2;
                            for (int counter3 = 0; counter3 < 20; counter3++)
                            {
                                currentValue[3] = counter3;
                                if (ValueIngredientMap.ContainsKey(getIngredientValueString(currentValue)))
                                {
                                    completeValueList.Add(getIngredientValueString(currentValue));
                                }
                            }
                        }
                    

                }
                else if (currentValue[3] == 0)
                { // 1.2.1.0
                    for (int counter3 = 0; counter3 < 20; counter3++)
                    {
                        currentValue[3] = counter3;
                        if (ValueIngredientMap.ContainsKey(getIngredientValueString(currentValue)))
                        {
                            completeValueList.Add(getIngredientValueString(currentValue));
                        }
                    }
                      
                }
                else
                {
                    completeValueList.Add(getIngredientValueString(currentValue));
                }

            }
            return completeValueList;
        }
    
       

        private static double getSimilarityScore(List<string> completeInputValueList, List<string> completeExaminedValueList)
        {
            double intersectionCount = SimilarityUtil.getIntersection(completeInputValueList, completeExaminedValueList);
            double unionCount = SimilarityUtil.getUnion(completeInputValueList, completeExaminedValueList, intersectionCount);
            if (intersectionCount == 0) return 0; // to avoid 0/0 = NaN
            else
            {
                return intersectionCount / completeInputValueList.Count;
                //return intersectionCount / unionCount;
            }
        }

        public static double getPenaltyScore(List<Ingredient> inputList, List<Ingredient> examinedList)
        {

           // List<string> completeInputValueList = getStringValueList(inputList);
           // List<string> completeExaminedValueList = getCompleteValueListInStringWithAncestor(examinedList);

            List<string> completeInputValueList = getCompleteValueListInStringWithChildren(inputList);
            List<string> completeExaminedValueList = getStringValueList(examinedList);

            return getPenaltyScore(completeInputValueList, completeExaminedValueList);

        }

        private static double getPenaltyScore(List<string> completeInputValueList, List<string> completeExaminedValueList)
        {
            double intersectionCount = SimilarityUtil.getIntersection(completeInputValueList, completeExaminedValueList);

            return intersectionCount;
        }

        public static string getIngredientValueString(int[] value)
        {
            return value[0] + "." + value[1] + "." + value[2] + "." + value[3];
        }

        public static void setVegetarianIngredientMap()
        {

            if (!File.Exists(VEGETARIAN_MAP_FILE_NAME))
            {
                Console.WriteLine("{0} does not exist.", VEGETARIAN_MAP_FILE_NAME);
            }
            using (StreamReader sr = File.OpenText(VEGETARIAN_MAP_FILE_NAME))
            {
                String input;
                while ((input = sr.ReadLine()) != null)
                {
                    string[] ingredients = input.Trim().Split(':');
                    string meat = ingredients[0].Trim().ToLower();
                    //Ingredient meat = getIngredientByName(ingredients[0].Trim());
                    Ingredient veg = getIngredientByName(ingredients[1].Trim());

                    List<Ingredient> ingredientList = new List<Ingredient>();
                    if (VegetarianIngredientMap.ContainsKey(meat))
                    {
                        ingredientList = VegetarianIngredientMap[meat]; 
                    }
                    ingredientList.Add(veg);
                    VegetarianIngredientMap.Add(meat, ingredientList);
                }
                Console.WriteLine("The end of the stream has been reached.");
            }
        }
        public static List<Ingredient> getVegetarianIngredient(Ingredient ingredient)
        {
            if (VegetarianIngredientMap.ContainsKey(ingredient.Name.ToLower()))
            {
                return VegetarianIngredientMap[ingredient.Name.ToLower()];
            }
            else
                return null;
            
        }
    }
   
}
